import javax.swing.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

/**
 * ****************************************************************************
 * $Author: Yegor Chemisov $
 * $Revision: 2.2 $
 * ****************************************************************************
 */

public class MemoryBoard implements ActionListener
{
    public static final int STATUS_ZERO     = 0;
    public static final int STATUS_GAME     = 1;
    public static final int STATUS_SHOW     = 2;
    public static final int STATUS_HIDE     = 3;

    private JPanel          mainPanel;
    private Cards           cards;
    private MemoryButton[]  buttons;
    private MemoryButton    firstButton;
    private MemoryButton    secondButton;
    private ResultsTable[]  results = null;

    private GameParameters  configuration;
    private long            startTime;
    private int             misses;
    private int             status;
    private Timer           timer;

    public MemoryBoard()
    {
        // Load game configuration
        configuration = new GameParameters();

        timer = new Timer(MAPConsts.HIDE_TIME, this);
        timer.setRepeats(false);

        // Load images for cards
        cards = new Cards();

        // Load Table of Results
        results = ResultsTable.loadResults();

        // Set default back image for all Memory Buttons
        MemoryButton.setBackImage(cards.getBackImage());

        //Create the main panel.
        mainPanel = new JPanel();
        mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.PAGE_AXIS));
        mainPanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));

        init();
        prepareGame();
    }

    public JPanel getMainPanel()
    {
        return mainPanel;    
    }

    public void init()
    {
        mainPanel.removeAll();

        buttons  = new MemoryButton[configuration.getNumButtons()];

        for (int i = 0; i < configuration.getNumRows(); i++)
        {
            JPanel row = new JPanel();
            row.setLayout(new BoxLayout(row, BoxLayout.X_AXIS));

            for (int j = 0; j < configuration.getNumCols(); j++)
            {
                int k = i * configuration.getNumCols() + j;
                buttons[k] = new MemoryButton(String.valueOf(k), this);
                row.add(buttons[k].getButton());
            }
            mainPanel.add(row);
        }
    }

    public void prepareGame()
    {
        int numButtons = configuration.getNumButtons();

        int[] img = new int[MAPConsts.NUM_IMAGES];
        int[] btn = new int[numButtons];

        // Select images to display
        for (int i = MAPConsts.NUM_IMAGES; i-- > 0; img[i] = i + 1);
        shuffle(img);

        for (int x = (numButtons) / 2, i = x; i-- > 0; btn[x + i] = img[i])
            btn[i] = img[i];
        shuffle(btn, numButtons);

        for (int i = numButtons; i-- > 0;)
        {
            buttons[i].setFaceImage(cards.getImageAt(btn[i]));
            buttons[i].turnOff();
        }

        firstButton = secondButton = null;
        misses = 0;

        StartGame.mainFrame.pack();

        status = STATUS_ZERO;
    }

    public synchronized void showAll()
    {
        for (int i = configuration.getNumButtons(); i-- > 0; buttons[i].turnOn());
        status = STATUS_SHOW;
    }

    public synchronized void hideAll()
    {
        if (status == STATUS_SHOW)
        {
            for (int i= configuration.getNumButtons(); i-- > 0; buttons[i].turnOff());
            status = STATUS_GAME;
            startTime = System.currentTimeMillis();
        }
    }

    public boolean isAllOpen()
    {
        for (int i= configuration.getNumButtons(); i-- > 0;)
            if (buttons[i].isOff()) return false;
        return true;
    }
    private static void shuffle(int arr[])
    {
        shuffle(arr,  arr.length);
    }

    private static void shuffle(int arr[], int len)
    {
        final int MAX_ROUNDS = 200;

        for (int i = MAX_ROUNDS; i > 0 ; i--)
        {
            int a = (int) (Math.random() * len);
            int b = (int) (Math.random() * len);
            int tmp = arr[a];
            arr[a] = arr[b];
            arr[b] = tmp;
        }
    }

    public void doClick(String action)
    {
        if (status == STATUS_ZERO)
        {
            new Flash(this, configuration.getSleepTime()).start();
            return;
        }

        if (status == STATUS_SHOW) return;

        // --- Status is Game ---
        MemoryButton currentButton = buttons[Integer.parseInt(action)];

        if (currentButton.isOff())
        {
            currentButton.turnOn();
            if (firstButton == null)
            {
                firstButton = currentButton;
            }
            else if (secondButton == null)
            {
                secondButton = currentButton;
                if (!firstButton.equals(secondButton)) timer.start();
            }
            else
            {
                hideTwoButtons();
                firstButton = currentButton;
                secondButton = null;
            }

            if (isAllOpen())
            {
                // End of Game
                PlayerResult result = new PlayerResult(misses, System.currentTimeMillis() - startTime);

                JOptionPane.showMessageDialog(StartGame.mainFrame,
                        MAPConsts.RES.getString(GlobalRes.MSG_MISSES) + result.getMisses() + "\n" +
                        MAPConsts.RES.getString(GlobalRes.MSG_TIME) + result.getSeconds()
                        + MAPConsts.RES.getString(GlobalRes.MSG_SECONDS),
                        MAPConsts.RES.getString(GlobalRes.RESULTS_TITLE),
                        JOptionPane.INFORMATION_MESSAGE);

                if (results[configuration.getLevel()].isResultGood(result))
                {
                    String playerName = (String) JOptionPane.showInputDialog(
                            StartGame.mainFrame,
                            MAPConsts.RES.getString(GlobalRes.MSG_ADD_NAME),
                            MAPConsts.RES.getString(GlobalRes.TITLE_ADD_NAME), 
                            JOptionPane.PLAIN_MESSAGE);

                    if (playerName != null && playerName.length() > 0)
                    {
                        result.setPlayerName(playerName);
                        results[configuration.getLevel()].addResult(result);
                        ResultsTable.saveResults(results);
                    }
                }

                hideAll();
                prepareGame();
            }
        }
    }

    public void actionPerformed(ActionEvent event)
    {
        String action  = event.getActionCommand();
        //System.out.println("Action " + action + " performed");
        //System.out.println("paramString " + event.paramString());

        if (event.getSource().getClass().equals(Timer.class) )
        {
            hideTwoButtons();
        }
        else if (action.equals(MAPActions.NEW))
        {
            prepareGame();
            new Flash(this, configuration.getSleepTime()).start();
        }
        else if (action.equals(MAPActions.EXIT))
        {
            System.exit(0);
        }
        else if (action.equals(MAPActions.TIMING))
        {
            // TODO: Add code
        }
        else if (action.equals(MAPActions.PLAYER))
        {
            // TODO: Add code
        }
        else if (action.equals(MAPActions.COMPUTER))
        {
            // TODO: Add code
        }
        else if (action.equals(MAPActions.CUSTOM))
        {
            configuration.showCustom();
            configuration.setLevel(GameParameters.CUSTOM);
            init();
            StartGame.mainFrame.pack();
            prepareGame();
        }
        else if (action.equals(MAPActions.BEST_TIMES))
        {
            results[configuration.getLevel()].showResults(StartGame.mainFrame);
        }
        else if (action.equals(MAPActions.ABOUT))
        {
            JOptionPane.showMessageDialog(StartGame.mainFrame,
                    MAPConsts.RES.getString(GlobalRes.MSG_ABOUT),
                    MAPConsts.RES.getString(GlobalRes.PROGRAMM_TITLE),
                    JOptionPane.INFORMATION_MESSAGE);
        }
        else if (action.startsWith(MAPActions.GRID_SIZE))
        {
            configuration.setLevel(Integer.parseInt(action.substring(MAPActions.GRID_SIZE.length())));
            init();
            StartGame.mainFrame.pack();
            prepareGame();
        }
        else
        {
            doClick(action);
        }
    }

    public synchronized void hideTwoButtons(int misses)
    {
        if (this.misses != misses) return;
        hideTwoButtons();
    }

    public synchronized void hideTwoButtons()
    {
        timer.stop();
        if ( firstButton != null &&
             secondButton != null &&
             !firstButton.equals(secondButton) )
        {
            misses++;
            firstButton.turnOff();
            secondButton.turnOff();
            firstButton = secondButton = null;
        }
    }
}
